package repodata

import (
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"path/filepath"
	"strings"
)

type LoaderLink struct {
}

// DataRepomd 用于存储 repomd.xml 中的每个数据块
type DataRepomd struct {
	Type     string `xml:"type,attr"`
	Checksum struct {
		Algorithm string `xml:"algo,attr"`
		Value     string `xml:",chardata"`
	} `xml:"checksum"`
	OpenChecksum struct {
		Algorithm string `xml:"algo,attr"`
		Value     string `xml:",chardata"`
	} `xml:"open-checksum"`
	Location struct {
		Href string `xml:"href,attr"`
	} `xml:"location"`
	Size     int64 `xml:"size"`
	OpenSize int64 `xml:"open-size"`
}

// RepomdXMLData 用于解析 repomd.xml 文件
type RepomdXMLData struct {
	XMLName xml.Name     `xml:"repomd"`
	Data    []DataRepomd `xml:"data"`
}

func (ri *RepoInfo) LoadRepomdXMLFromLink(ctx Context, repoLink string) (RepomdXMLData, error) {
	repomdURL := strings.TrimSuffix(repoLink, "/") + "/repodata/repomd.xml"
	resp, err := http.Get(repomdURL)
	defer closeObj(resp.Body)
	if err != nil {
		return RepomdXMLData{}, fmt.Errorf("cannot find repomd.xml: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return RepomdXMLData{}, fmt.Errorf("cannot download repomd.xml: HTTP StatusCode %d", resp.StatusCode)
	}

	data, err := io.ReadAll(resp.Body)
	if err != nil {
		return RepomdXMLData{}, fmt.Errorf("cannot read repomd.xml: %v", err)
	}

	var repomd RepomdXMLData
	err = xml.Unmarshal(data, &repomd)
	if err != nil {
		return RepomdXMLData{}, fmt.Errorf("cannot parse repomd.xml: %v", err)
	}

	return repomd, nil
}

func (ri *RepoInfo) DownloadSqliteFromLink(ctx Context, linkSqlite string) (string, error) {
	filePathSqlite := filepath.Join(ri.workspace, filepath.Base(linkSqlite))
	return filePathSqlite, DownloadFileFromLink(ctx, linkSqlite, filePathSqlite)
}

// DecompressSqlite 自动解压
func (ri *RepoInfo) DecompressSqlite(ctx Context, path2sqliteCompress string) (string, error) {
	if strings.HasSuffix(path2sqliteCompress, ".bz2") {
		path2sqlite, _ := strings.CutSuffix(path2sqliteCompress, ".bz2")
		return path2sqlite, DecompressBZ2(path2sqliteCompress, path2sqlite)
	}
	return "", fmt.Errorf("unknown compress type")
}

// FindPrimaryDBLinkFromRepodataLink 从给定的仓库链接中寻找primary_db.sqlite文件的链接
func (ri *RepoInfo) FindPrimaryDBLinkFromRepodataLink(ctx Context, repoLink string) (string, error) {
	repomdData, err := ri.LoadRepomdXMLFromLink(ctx, repoLink)
	if err != nil {
		return "", fmt.Errorf("FindPrimaryDBLinkFromRepodataLink.%v", err)
	}
	for _, fileInfo := range repomdData.Data {
		if fileInfo.Type == "primary_db" {
			return strings.TrimSuffix(repoLink, "/") + "/" + strings.TrimPrefix(fileInfo.Location.Href, "/"), nil
		}
	}
	return "", fmt.Errorf("cannot find primary_db from [%v]", repoLink)
}

// DownloadPrimaryDBFromLink 从给定的仓库链接中下载primary.sqlite文件到本地，并返回sqlite文件路径
func (ri *RepoInfo) DownloadPrimaryDBFromLink(ctx Context, repoLink string) (string, error) {
	linkPrimaryDb, err := ri.FindPrimaryDBLinkFromRepodataLink(ctx, repoLink)
	if err != nil {
		return "", fmt.Errorf("DownloadPrimaryDBFromLink.%v", err)
	}
	primaryDBPath, err := ri.DownloadSqliteFromLink(ctx, linkPrimaryDb)
	if err != nil {
		return "", fmt.Errorf("DownloadPrimaryDBFromLink.%v", err)
	}
	return ri.DecompressSqlite(ctx, primaryDBPath)
}
